
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <winsock.h>
#pragma comment(lib, "ws2_32")



bool getServerIp(uint32_t* ipv4, uint16_t* port) {          // 获取物链域名服务器IP
    char svrName[256] = "i.ip71.cn";
    HOSTENT* svrHost = gethostbyname(svrName);
    if (svrHost != NULL) {
        memcpy(ipv4, svrHost->h_addr_list[0], sizeof(uint32_t));
        memcpy(port, svrHost->h_addr_list[1], sizeof(uint16_t));
        return true;
    }
    return false;
}
int textArray(size_t* array, char* text, int length, char key) {	// 从字符串转换到阵列
    int index = 1;
    int offset = 1;
    array[0] = (size_t)text;
    for (; index < length; index++) {
        if (text[index] == key) {					// 如果text是const空间 text[index] = 0; 会死掉！
            text[index] = 0;						// 找到key 变为字符串结尾
            array[offset] = (size_t)text + index + 1;
            offset += 1;
        }
    }
    return offset / 1;
}
void printfError(char* text) {						// 出错显示的字眼
	switch (text[1]){
	case '1':	printf("一般出错\r\n"); return; 
	case '2':	printf("指令格式不对\r\n"); return;
	case '3':	printf("参数出错\r\n"); return;
	case '4':	printf("账号名出错\r\n"); return;
	case '5':	printf("账号密码出错\r\n"); return;
	case '6':	printf("电器设备账号名出错\r\n"); return;
	case '7':	printf("账号访问码出错\r\n"); return;
	case '9':	printf("登录令牌或访问令牌出错\r\n"); return;	
	}
}
bool LoginToServer(SOCKET udp, SOCKADDR_IN* addr, char* an, char* pw, char* outToken, uint32_t* outTime) {
    char bytes[128];
    struct sockaddr_in dest_addr = { 0 };
    int struct_len = sizeof(dest_addr);

    int length = snprintf(bytes, sizeof(bytes), "ct:ls&an:%s&pw:%s", an, pw);
    sendto(udp, bytes, length, 0, (struct sockaddr*)addr, sizeof(SOCKADDR_IN));
    bytes[length] = 0;
    printf("发送%s到服务器\r\n", bytes);
    length = recvfrom(udp, bytes, sizeof(bytes), 0, (struct sockaddr*)&dest_addr, &struct_len);
    if (length > 0) {
        char* array[8];
        bytes[length] = 0;
        printf("从服务器接收到%s\r\n", bytes);
        int lineQty = textArray((size_t*)array, bytes, length, '&');	// 从字符串转换到阵列
		if (!memcmp(array[0], "ls", 2)) {								// 判断array[0]是否是ls字眼
			if (!memcmp(array[1], "ok", 2)) {							// 判断array[1]是否是ok字眼
				if (lineQty >= 4) {
                    int tLen = (int)strlen(array[3]);
                    memcpy(outToken, array[3], tLen+1);
                    *outTime = atoi(array[2]);
                    return true;
                }
                printf("出错。没有从服务器接收到数据\r\n");
                return false;
            }
			printfError(array[1]);										// 出错显示的字眼
			return false;
        }
		printf("出错。服务器接收到数据%d\r\n", array[0]);
        return false;
    }
	printf("出错。没有从服务器接收到数据\r\n");
	return false;
}
bool SyncHeartbeat(SOCKET udp, SOCKADDR_IN* addr, char* token, char* outIp) {
    char bytes[128];
    int retry = 3;
    struct sockaddr_in dest_addr = { 0 };
    int struct_len = sizeof(dest_addr);

    int length = snprintf(bytes, sizeof(bytes), "ct:sh&lt:%s", token);
    sendto(udp, bytes, length, 0, (struct sockaddr*)addr, sizeof(SOCKADDR_IN));
    bytes[length] = 0;
    printf("发送心跳包(%s)到服务器\r\n", bytes);
    length = recvfrom(udp, bytes, sizeof(bytes), 0, (struct sockaddr*)&dest_addr, &struct_len);
    if (length > 0) {
        char* array[8];
        bytes[length] = 0;
        printf("从服务器接收到%s\r\n", bytes);
        int lineQty = textArray((size_t*)array, bytes, length, '&');	// 从字符串转换到阵列
        if (lineQty >= 3) {
            if (!memcmp(array[0], "sh", 2)) {
                if (!memcmp(array[1], "ok", 2)) {
                    int tLen = (int)strlen(array[2]);
                    memcpy(outIp, array[2], tLen + 1);
                    return true;
                }
                printf("出错。没有从服务器接收到数据\r\n");
                return false;
            }
        }
    }
    return true;
}

int main(int argc, char* argv[]) {
    WSADATA wSAData;
    uint32_t svrIp;
    uint16_t svrPort;
    SOCKET udp;
    uint8_t* ip = (uint8_t*)&svrIp;

    if (WSAStartup(0x0101, &wSAData) != 0) {                // 初始化网络
        printf("初始化网络出错\r\n");
        return 1;
    }
    if (!getServerIp(&svrIp, &svrPort)) {                   // 获取物链域名服务器IP
        printf("获取物链域名服务器IP出错\r\n");
        return 1;
    }
    printf("获取物链域名服务器IP(%d.%d.%d.%d:%d)成功\r\n", ip[0], ip[1], ip[2], ip[3], svrPort);
    if (argc < 3) {                                         // 没有输入域名账号名及密码
        printf("没有输入域名账号名及密码，至少需要3个参数，现在只有%d个参数\r\n", argc);
        return 1;
    }
    udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);         // 创建必要网络参数
    if (udp != SOCKET_ERROR) {  //创建必要网络参数
        uint32_t time;
        char token[16];
        SOCKADDR_IN addr = { 0 };
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = svrIp;
        addr.sin_port = htons(svrPort);
        if (LoginToServer(udp, &addr, argv[1], argv[2], token, &time)) {
            char outIp[16];
            printf("登录物链域名服务器(token:%s 间隔时间:%d秒)成功\r\n", token, time);
            if (SyncHeartbeat(udp, &addr, token, outIp)) {
                printf("获得本机的网络IP为：%s\r\n", outIp);
            }
        }
        WSACleanup();
        return 0;
    }
    WSACleanup();
    return 1;
}